[XEN] gnttab: Add new op unmap_and_replace
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 30 May 2007 09:45:44 +0000 (10:45 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 30 May 2007 09:45:44 +0000 (10:45 +0100)
The operation unmap_and_replace is an extension of unmap_grant_ref.
A new argument in the form of a virtual address (for PV) is given.
Instead of modifying the PTE for the mapped grant table entry to
null, we change it to the PTE for the new address.  In turn we
point the new address to null.

As it stands grant table entries once mapped cannot be
remapped by the guest OS (it can however perform a new
mapping on the same entry but that is within our control).
Therefore it's safe to manipulate the mapped PTE entry to
redirect it to a normal page where we've copied the contents.

It's intended to be used as follows:

1) map_grant_ref to v1
2) ...
3) alloc page at v2
4) copy the page at v1 to v2
5) unmap_and_replace v1 with v2

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Added compat integration (PAE-on-64).

Signed-off-by: Keir Fraser <keir@xensource.com>
linux-2.6-xen-sparse/include/xen/gnttab.h
xen/arch/ia64/xen/mm.c
xen/arch/powerpc/mm.c
xen/arch/x86/mm.c
xen/common/compat/grant_table.c
xen/common/grant_table.c
xen/include/asm-ia64/grant_table.h
xen/include/asm-powerpc/grant_table.h
xen/include/asm-x86/grant_table.h
xen/include/public/grant_table.h
xen/include/xlat.lst

index 93a6e2541e5734285245970f3930997f2deacdd7..9ccd67fe02b70847a60bfb79cca08a87749f8ae1 100644 (file)
@@ -135,4 +135,19 @@ gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, maddr_t addr,
        unmap->dev_bus_addr = 0;
 }
 
+static inline void
+gnttab_set_replace_op(struct gnttab_unmap_and_replace *unmap, maddr_t addr,
+                     maddr_t new_addr, grant_handle_t handle)
+{
+       if (xen_feature(XENFEAT_auto_translated_physmap)) {
+               unmap->host_addr = __pa(addr);
+               unmap->new_addr = __pa(new_addr);
+       } else {
+               unmap->host_addr = addr;
+               unmap->new_addr = new_addr;
+       }
+
+       unmap->handle = handle;
+}
+
 #endif /* __ASM_GNTTAB_H__ */
index fdcfecb6b73344c02aa60729b8ece5d4b8beee13..78245046279f467efe73975dda4f02fb20060937 100644 (file)
@@ -63,7 +63,7 @@
  *     assign_domain_page_replace()
  *   - cmpxchg p2m entry
  *     assign_domain_page_cmpxchg_rel()
- *     destroy_grant_host_mapping()
+ *     replace_grant_host_mapping()
  *     steal_page()
  *     zap_domain_page_one()
  *   - read p2m entry
  * - races between p2m entry update and tlb insert
  *   This is a race between reading/writing the p2m entry.
  *   reader: vcpu_itc_i(), vcpu_itc_d(), ia64_do_page_fault(), vcpu_fc()
- *   writer: assign_domain_page_cmpxchg_rel(), destroy_grant_host_mapping(), 
+ *   writer: assign_domain_page_cmpxchg_rel(), replace_grant_host_mapping(), 
  *           steal_page(), zap_domain_page_one()
  * 
  *   For example, vcpu_itc_i() is about to insert tlb by calling
  *   This is a race between reading/writing the p2m entry.
  *   reader: vcpu_get_domain_bundle(), vmx_get_domain_bundle(),
  *           efi_emulate_get_time()
- *   writer: assign_domain_page_cmpxchg_rel(), destroy_grant_host_mapping(), 
+ *   writer: assign_domain_page_cmpxchg_rel(), replace_grant_host_mapping(), 
  *           steal_page(), zap_domain_page_one()
  * 
  *   A page which assigned to a domain can be de-assigned by another vcpu.
@@ -1512,8 +1512,8 @@ create_grant_host_mapping(unsigned long gpaddr,
 
 // grant table host unmapping
 int
-destroy_grant_host_mapping(unsigned long gpaddr,
-               unsigned long mfn, unsigned int flags)
+replace_grant_host_mapping(unsigned long gpaddr,
+              unsigned long mfn, unsigned long new_gpaddr, unsigned int flags)
 {
     struct domain* d = current->domain;
     unsigned long gpfn = gpaddr >> PAGE_SHIFT;
@@ -1524,6 +1524,11 @@ destroy_grant_host_mapping(unsigned long gpaddr,
     pte_t old_pte;
     struct page_info* page = mfn_to_page(mfn);
 
+    if (new_gpaddr) {
+        gdprintk(XENLOG_INFO, "%s: new_gpaddr 0x%lx\n", __func__, new_gpaddr);
+       return GNTST_general_error;
+    }
+
     if (flags & (GNTMAP_application_map | GNTMAP_contains_pte)) {
         gdprintk(XENLOG_INFO, "%s: flags 0x%x\n", __func__, flags);
         return GNTST_general_error;
@@ -1571,7 +1576,7 @@ destroy_grant_host_mapping(unsigned long gpaddr,
     BUG_ON(pte_pgc_allocated(old_pte));
     domain_page_flush_and_put(d, gpaddr, pte, old_pte, page);
 
-    perfc_incr(destroy_grant_host_mapping);
+    perfc_incr(replace_grant_host_mapping);
     return GNTST_okay;
 }
 
index 2dec97ad1b9fed4aaaed9f2ad6b2d2ea0d643f96..51a4e54ed797a8fa60e37ff2189a88da532ec24d 100644 (file)
@@ -183,9 +183,16 @@ int create_grant_host_mapping(
     return create_grant_va_mapping(addr, frame, current);
 }
 
-int destroy_grant_host_mapping(
-    unsigned long addr, unsigned long frame, unsigned int flags)
+int replace_grant_host_mapping(
+    unsigned long addr, unsigned long frame, unsigned long new_addr,
+    unsigned int flags)
 {
+    if (new_addr)
+        printk("%s: new_addr not supported\n", __func__);
+        BUG();
+        return GNTST_general_error;
+    }
+
     if (flags & GNTMAP_contains_pte) {
         printk("%s: GNTMAP_contains_pte not supported\n", __func__);
         BUG();
index 863245aae33fda9d9e25da1a8c40621e52befb0d..7f6ca30c2f62cf8288cf15743d700787b679780d 100644 (file)
@@ -2621,8 +2621,8 @@ static int create_grant_va_mapping(
     return GNTST_okay;
 }
 
-static int destroy_grant_va_mapping(
-    unsigned long addr, unsigned long frame, struct vcpu *v)
+static int replace_grant_va_mapping(
+    unsigned long addr, unsigned long frame, l1_pgentry_t nl1e, struct vcpu *v)
 {
     l1_pgentry_t *pl1e, ol1e;
     unsigned long gl1mfn;
@@ -2646,7 +2646,7 @@ static int destroy_grant_va_mapping(
     }
 
     /* Delete pagetable entry. */
-    if ( unlikely(!UPDATE_ENTRY(l1, pl1e, ol1e, l1e_empty(), gl1mfn, v)) )
+    if ( unlikely(!UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, gl1mfn, v)) )
     {
         MEM_LOG("Cannot delete PTE entry at %p", (unsigned long *)pl1e);
         rc = GNTST_general_error;
@@ -2658,6 +2658,12 @@ static int destroy_grant_va_mapping(
     return rc;
 }
 
+static int destroy_grant_va_mapping(
+    unsigned long addr, unsigned long frame, struct vcpu *v)
+{
+    return replace_grant_va_mapping(addr, frame, l1e_empty(), v);
+}
+
 int create_grant_host_mapping(
     uint64_t addr, unsigned long frame, unsigned int flags)
 {
@@ -2673,12 +2679,48 @@ int create_grant_host_mapping(
     return create_grant_va_mapping(addr, pte, current);
 }
 
-int destroy_grant_host_mapping(
-    uint64_t addr, unsigned long frame, unsigned int flags)
+int replace_grant_host_mapping(
+    uint64_t addr, unsigned long frame, uint64_t new_addr, unsigned int flags)
 {
+    l1_pgentry_t *pl1e, ol1e;
+    unsigned long gl1mfn;
+    int rc;
+    
     if ( flags & GNTMAP_contains_pte )
-        return destroy_grant_pte_mapping(addr, frame, current->domain);
-    return destroy_grant_va_mapping(addr, frame, current);
+    {
+       if (!new_addr)
+           return destroy_grant_pte_mapping(addr, frame, current->domain);
+
+       MEM_LOG("Unsupported grant table operation");
+       return GNTST_general_error;
+    }
+
+    if (!new_addr)
+       return destroy_grant_va_mapping(addr, frame, current);
+
+    pl1e = guest_map_l1e(current, new_addr, &gl1mfn);
+    if ( !pl1e )
+    {
+        MEM_LOG("Could not find L1 PTE for address %lx",
+                (unsigned long)new_addr);
+        return GNTST_general_error;
+    }
+    ol1e = *pl1e;
+
+    if ( unlikely(!UPDATE_ENTRY(l1, pl1e, ol1e, l1e_empty(), gl1mfn, current)) )
+    {
+        MEM_LOG("Cannot delete PTE entry at %p", (unsigned long *)pl1e);
+        guest_unmap_l1e(current, pl1e);
+        return GNTST_general_error;
+    }
+
+    guest_unmap_l1e(current, pl1e);
+
+    rc = replace_grant_va_mapping(addr, frame, ol1e, current);
+    if ( rc && !paging_mode_refcounts(current->domain) )
+        put_page_from_l1e(ol1e, current->domain);
+
+    return rc;
 }
 
 int steal_page(
index aea0bfe78eba624dc66f23829e93ac8a7736ef3c..342e35622da4ece761fb076b66c5212a33f7b127 100644 (file)
@@ -17,6 +17,10 @@ CHECK_gnttab_map_grant_ref;
 CHECK_gnttab_unmap_grant_ref;
 #undef xen_gnttab_unmap_grant_ref
 
+#define xen_gnttab_unmap_and_replace gnttab_unmap_and_replace
+CHECK_gnttab_unmap_and_replace;
+#undef xen_gnttab_unmap_and_replace
+
 DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_compat_t);
 DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_compat_t);
 DEFINE_XEN_GUEST_HANDLE(gnttab_copy_compat_t);
@@ -50,6 +54,10 @@ int compat_grant_table_op(unsigned int cmd,
     CASE(unmap_grant_ref);
 #endif
 
+#ifndef CHECK_gnttab_unmap_and_replace
+    CASE(unmap_and_replace);
+#endif
+
 #ifndef CHECK_gnttab_setup_table
     CASE(setup_table);
 #endif
index 40df1035fcaa19a90916d5202a8896c135c06b18..d6581f66cfe7885a394749934ebb3952d469c39c 100644 (file)
@@ -58,6 +58,16 @@ union grant_combo {
     } shorts;
 };
 
+/* Used to share code between unmap_grant_ref and unmap_and_replace. */
+struct gnttab_unmap_common {
+    uint64_t host_addr;
+    uint64_t dev_bus_addr;
+    uint64_t new_addr;
+    grant_handle_t handle;
+
+    int16_t status;
+};
+
 #define PIN_FAIL(_lbl, _rc, _f, _a...)          \
     do {                                        \
         gdprintk(XENLOG_WARNING, _f, ## _a );   \
@@ -397,8 +407,8 @@ gnttab_map_grant_ref(
 }
 
 static void
-__gnttab_unmap_grant_ref(
-    struct gnttab_unmap_grant_ref *op)
+__gnttab_unmap_common(
+    struct gnttab_unmap_common *op)
 {
     domid_t          dom;
     grant_ref_t      ref;
@@ -477,8 +487,8 @@ __gnttab_unmap_grant_ref(
 
     if ( (op->host_addr != 0) && (flags & GNTMAP_host_map) )
     {
-        if ( (rc = destroy_grant_host_mapping(op->host_addr,
-                                              frame, flags)) < 0 )
+        if ( (rc = replace_grant_host_mapping(op->host_addr,
+                                              frame, op->new_addr, flags)) < 0 )
             goto unmap_out;
 
         ASSERT(act->pin & (GNTPIN_hstw_mask | GNTPIN_hstr_mask));
@@ -518,6 +528,20 @@ __gnttab_unmap_grant_ref(
     rcu_unlock_domain(rd);
 }
 
+static void
+__gnttab_unmap_grant_ref(
+    struct gnttab_unmap_grant_ref *op)
+{
+    struct gnttab_unmap_common common = {
+       .host_addr = op->host_addr,
+       .dev_bus_addr = op->dev_bus_addr,
+       .handle = op->handle,
+    };
+
+    __gnttab_unmap_common(&common);
+    op->status = common.status;
+}
+
 static long
 gnttab_unmap_grant_ref(
     XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t) uop, unsigned int count)
@@ -542,6 +566,44 @@ fault:
     return -EFAULT;    
 }
 
+static void
+__gnttab_unmap_and_replace(
+    struct gnttab_unmap_and_replace *op)
+{
+    struct gnttab_unmap_common common = {
+       .host_addr = op->host_addr,
+       .new_addr = op->new_addr,
+       .handle = op->handle,
+    };
+
+    __gnttab_unmap_common(&common);
+    op->status = common.status;
+}
+
+static long
+gnttab_unmap_and_replace(
+    XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t) uop, unsigned int count)
+{
+    int i;
+    struct gnttab_unmap_and_replace op;
+
+    for ( i = 0; i < count; i++ )
+    {
+        if ( unlikely(__copy_from_guest_offset(&op, uop, i, 1)) )
+            goto fault;
+        __gnttab_unmap_and_replace(&op);
+        if ( unlikely(__copy_to_guest_offset(uop, i, &op, 1)) )
+            goto fault;
+    }
+
+    flush_tlb_mask(current->domain->domain_dirty_cpumask);
+    return 0;
+
+fault:
+    flush_tlb_mask(current->domain->domain_dirty_cpumask);
+    return -EFAULT;    
+}
+
 int
 gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
 {
@@ -1205,6 +1267,21 @@ do_grant_table_op(
         rc = gnttab_unmap_grant_ref(unmap, count);
         break;
     }
+    case GNTTABOP_unmap_and_replace:
+    {
+        XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t) unmap =
+            guest_handle_cast(uop, gnttab_unmap_and_replace_t);
+        if ( unlikely(!guest_handle_okay(unmap, count)) )
+            goto out;
+        rc = -EPERM;
+        if ( unlikely(!grant_operation_permitted(d)) )
+            goto out;
+        rc = -ENOSYS;
+        if ( unlikely(!replace_grant_supported()) )
+            goto out;
+        rc = gnttab_unmap_and_replace(unmap, count);
+        break;
+    }
     case GNTTABOP_setup_table:
     {
         rc = gnttab_setup_table(
index f10d92e4194f473cf774a65b47145486ded8043c..ba9ff72a9eb972c8829bf039c3ed7903da01e7b2 100644 (file)
@@ -9,7 +9,7 @@
 
 // for grant map/unmap
 int create_grant_host_mapping(unsigned long gpaddr, unsigned long mfn, unsigned int flags);
-int destroy_grant_host_mapping(unsigned long gpaddr, unsigned long mfn, unsigned int flags);
+int replace_grant_host_mapping(unsigned long gpaddr, unsigned long mfn, unsigned long new_gpaddr, unsigned int flags);
 
 // for grant transfer
 void guest_physmap_add_page(struct domain *d, unsigned long gpfn, unsigned long mfn);
@@ -67,4 +67,9 @@ static inline void gnttab_clear_flag(unsigned long nr, uint16_t *addr)
 #define gnttab_release_put_page(page)           put_page((page))
 #define gnttab_release_put_page_and_type(page)  put_page_and_type((page))
 
+static inline int replace_grant_supported(void)
+{
+    return 0;
+}
+
 #endif /* __ASM_GRANT_TABLE_H__ */
index e8ca87cbb404b3ffff9211e1180c95c31130ca26..96527bc5c7834cdefc162bf4f4045947e16e2ce6 100644 (file)
@@ -35,8 +35,9 @@ extern long pte_remove(ulong flags, ulong ptex, ulong avpn,
 
 int create_grant_host_mapping(
     unsigned long addr, unsigned long frame, unsigned int flags);
-int destroy_grant_host_mapping(
-    unsigned long addr, unsigned long frame, unsigned int flags);
+int replace_grant_host_mapping(
+    unsigned long addr, unsigned long frame, unsigned long new_addr,
+    unsigned int flags);
 
 #define gnttab_create_shared_page(d, t, i)                               \
     do {                                                                 \
@@ -82,4 +83,8 @@ static inline uint cpu_foreign_map_order(void)
 #define gnttab_release_put_page_and_type(page)  do { } while (0)
 #endif
 
+static inline int replace_grant_supported(void)
+{
+    return 0;
+}
 #endif  /* __ASM_PPC_GRANT_TABLE_H__ */
index f0c49bac3bda001f53ad1aed9079eab57d2e33a1..b79b003fa1e0b8021d31cadb0bc7eb32c79f5f80 100644 (file)
@@ -15,8 +15,8 @@
  */
 int create_grant_host_mapping(
     uint64_t addr, unsigned long frame, unsigned int flags);
-int destroy_grant_host_mapping(
-    uint64_t addr, unsigned long frame, unsigned int flags);
+int replace_grant_host_mapping(
+    uint64_t addr, unsigned long frame, uint64_t new_addr, unsigned int flags);
 
 #define gnttab_create_shared_page(d, t, i)                               \
     do {                                                                 \
@@ -48,4 +48,9 @@ static inline void gnttab_clear_flag(unsigned long nr, uint16_t *addr)
         /* Done implicitly when page tables are destroyed. */   \
     } while (0)
 
+static inline int replace_grant_supported(void)
+{
+    return 1;
+}
+
 #endif /* __ASM_GRANT_TABLE_H__ */
index 222ac37b8688591e15430a86a07fc5b000393a4b..bae0bd1a05651ab8b3916d08e34b534b4cbeadb7 100644 (file)
@@ -328,6 +328,29 @@ struct gnttab_query_size {
 typedef struct gnttab_query_size gnttab_query_size_t;
 DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t);
 
+/*
+ * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings
+ * tracked by <handle> but atomically replace the page table entry with one
+ * pointing to the machine address under <new_addr>.  <new_addr> will be
+ * redirected to the null entry.
+ * NOTES:
+ *  1. The call may fail in an undefined manner if either mapping is not
+ *     tracked by <handle>.
+ *  2. After executing a batch of unmaps, it is guaranteed that no stale
+ *     mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_and_replace    7
+struct gnttab_unmap_and_replace {
+    /* IN parameters. */
+    uint64_t host_addr;
+    uint64_t new_addr;
+    grant_handle_t handle;
+    /* OUT parameters. */
+    int16_t  status;              /* GNTST_* */
+};
+typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t);
+
 
 /*
  * Bitfield values for update_pin_status.flags.
index 727fdd4292c384ba20894da548e39e7eed0be735..5e6ebe8c5981c49484069946e529d9e4fd9298f9 100644 (file)
@@ -26,6 +26,7 @@
 !      gnttab_setup_table              grant_table.h
 !      gnttab_transfer                 grant_table.h
 ?      gnttab_unmap_grant_ref          grant_table.h
+?      gnttab_unmap_and_replace        grant_table.h
 ?      grant_entry                     grant_table.h
 ?      kexec_exec                      kexec.h
 !      kexec_image                     kexec.h